<!DOCTYPE HTML>
<html>

<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>React 生命周期函数深入浅出 | DeepThought</title>
  <meta name="google-site-verification" content="Ei6mmDdf-cJwox1RvJOeX8Dc720fqWjZQu-Ww2BjBt4">
  
  <meta name="author" content="积慕">
  
  <meta name="description" content="react,前端,iis,生命周期,lifecycle,基础知识,概念,基本概念,react15,react16,react 15,react 16.,react 16,生命,周期">
  
  
  <meta name="keywords" content="react,前端,iis,生命周期,lifecycle,基础知识,概念,基本概念,react15,react16,react 15,react 16.,react 16,生命,周期">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
  <meta property="og:title" content="React 生命周期函数深入浅出">
  <meta property="og:site_name" content="DeepThought">

  
  <meta property="og:image" content>
  

  <link href="https://pic.zhuliang.ltd/favicon.ico" rel="shortcut icon" type="image/x-icon">
  <link rel="alternate" href="/atom.xml" title="DeepThought" type="application/atom+xml">
  <link rel="stylesheet" href="/css/style.css" media="screen" type="text/css">

  <link rel="stylesheet" href="//at.alicdn.com/t/font_1187293_8fec2ou5fyx.css">

  <!-- <link rel="stylesheet" href="//cdn.bootcss.com/highlight.js/9.6.0/styles/github.min.css"> -->
  <!-- <script src="//cdn.bootcss.com/highlight.js/9.6.0/highlight.min.js"></script> -->
  <script src="/js/highlight.min.js"></script>

  <link rel="stylesheet" href="/css/markdown.css">

  <script src="/js/av-min.3.0.4.js"></script>
  <script src="/js/Valine.min.1.3.6.js"></script>

</head></html>

<body>
  <header id="header" class="inner"><div class="alignleft">
  <h1><a href="/">DeepThought</a></h1>
  <h2><a href="/">知识需沉淀，更需分享</a></h2>
</div>
<nav id="main-nav" class="alignright">
  <ul>
    
      <li><a href="/">首页</a></li>
    
      <li><a href="/about">关于我</a></li>
    
      <li><a href="/atom.xml">订阅RSS</a></li>
    
  </ul>
  <div class="clearfix"></div>
</nav>
<div class="clearfix"></div>
        </header>
  <div id="content" class="inner">
    <div id="main-col" class="alignleft"><div id="wrapper"><article class="post">
  
  <div class="post-content">
    <header>
      
        <div class="icon"></div>
      

 
      
  
    <h1 class="title">React 生命周期函数深入浅出</h1>
  


      
        <time datetime="2018-10-13T02:01:49.000Z">
          create: 2018-10-13 10:01:49 | update: 2018-10-13 10:01:49
            <span style="float: right;">
    <span class="post-count" id="busuanzi_container_page_pv" style="display: inline;">
        
        本文总阅读量: <span id="busuanzi_value_page_pv"></span>&nbsp;次
        <span class="blueSeparate">&nbsp;|&nbsp;</span>
        
        <span class="post-count">文章总字数:&nbsp;4.8k&nbsp;字</span>
    <span class="blueSeparate">&nbsp;|&nbsp;</span>
    <span class="post-count">阅读约需:&nbsp;17&nbsp;分钟</span>
    <br>
</span>
</span>
        </time>
      
            
    </header>
    <div class="entry">
      
      <blockquote>
<p>本篇博文基于 React 16.5.2</p>
</blockquote>
<h1 id="吐槽："><a href="#吐槽：" class="headerlink" title="吐槽："></a>吐槽：</h1><p>作为一个后端开发，15 年开始关注大前端发展趋势，于 17 年去线下听了场前端开发会议，那个时候 Vue2.0 刚出没多久，就被那快速构建页面给吸引了。最早重返前端还是大半年前，新项目用vue写了几个功能页面，发现现在写前端是真挺舒服，尤其是对于后端人员来说（排除掉 CSS ），快速入门并上手不是什么问题。</p>
<p>至于为什么最终选择了 react 而非 vue ？是因为当时对 react 和 vue 及 RN 和 weex 做了番调研对比，鉴于 weex 的不给力，及后期 react 和 vue 学习成本差不多，但 react 的社区更为活跃，外加发起者背景，就毅然选择了 react 。（我个人是通一精百的支持者，所以对于 react 的理念（learn once，write anywhere），是很赞成的。而 weex 的理念 (write once，run anywhere) 虽然很吸引人，但时下个人觉得技术并未达到此程度，配置的复杂度及大量的轮子需造，难以满足大型项目的要求。包括目前 JD 推出的 Taro，个人目前持观望态度，等到 react 这块应用到项目之后，再码一波 Taro 实际调研一番）。</p>
<p>接触 React 的时候已经是 React 16.3 ，不禁感慨前端发展至今，越有后端的趋势。前后花了3个多月的时间过了一遍 webpack 4， npm ， react ，在公司内部做了几场培训，发现了其中的一些不协调，但随着版本的迭代，这些不协调也依次在被更正。（看 React 17 的更新内容，原本一些摸棱两可的方法、属性元素命名均会得到改善：）</p>
<h1 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h1><p>本篇博文会对 React 16.5.2 中的常用生命周期函数做一些翻译、讲解说明及发表一些个人的理解和看法，如有错误、歧义之处，还望大家不吝啬指出，互相交流：）</p>
<p>开篇借用官方提供的生命周期函数图：（虽然是 React 16.4 的，但同样适用于 16.5 版本）</p>
<a id="more"></a>
<p><img src="https://pic.zhuliang.ltd/1101407-20181009160307721-2071012737.png-c" alt></p>
<p>出处：<a href="http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/" target="_blank" rel="noopener">http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/</a></p>
<p>react 组件生命周期函数说明官方地址：<a href="https://reactjs.org/docs/react-component.html" target="_blank" rel="noopener">https://reactjs.org/docs/react-component.html</a></p>
<blockquote>
<p>getDerivedStateFromProps()方法的 16.3 版本与之后的 16.4 和 16.5 有所调整：</p>
<ul>
<li><p>在 16.3 版本上，该方法仅在 props 变动时才会被触发。</p>
</li>
<li><p>在 16.3 之后的版本上，该方法调整为 props 和 state 的变动都会触发该方法。</p>
</li>
</ul>
</blockquote>
<p>从上图中，我们可以看到，React 的生命周期函数主要集中在三个阶段：挂载阶段（Mounting），更新阶段（Updating）和卸载阶段（Unmounting）。</p>
<p>其中：</p>
<ul>
<li>Mounting阶段主要有这几个方法需要注意：<ul>
<li>constructor(props) </li>
<li>componentWillReceiveProps(nextProps)</li>
<li>getDerivedStateFromProps(props, state)</li>
<li>componentWillMount()</li>
<li>render()</li>
<li>componentDidMount()</li>
</ul>
</li>
<li>Updating阶段主要有这几个方法需要注意：<ul>
<li>componentWillReceiveProps(nextProps)</li>
<li>getDerivedStateFromProps(props, state)</li>
<li>shouldComponentUpdate()</li>
<li>componentWillUpdate()</li>
<li>render()</li>
<li>componentDidUpdate()</li>
</ul>
</li>
<li>Unmounting阶段只有一个方法需要注意：<ul>
<li>componentWillUnmount()</li>
</ul>
</li>
<li>额外的几个方法说明：<ul>
<li>componentDidCatch()</li>
<li>this.setState()</li>
<li>this.forceUpdate()</li>
</ul>
</li>
</ul>
<p>下面我们就对以上生命周期函数做进一步说明：</p>
<h1 id="Mounting："><a href="#Mounting：" class="headerlink" title="Mounting："></a>Mounting：</h1><h2 id="constructor-props"><a href="#constructor-props" class="headerlink" title="constructor(props)"></a>constructor(props)</h2><ul>
<li>在构造函数中，一般就做两件事情：<ol>
<li>初始化 state<ul>
<li>在构造函数中初始化 state 需直接赋值，不能调用 this.setState() 方法。</li>
<li>constructor() 是唯一能够让你直接给 this.state 赋值的地方，可能有同学会说可以在构造函数外(跟 constructor 同级)直接给 state 赋值，确实，因为 state 派生自 Component 或者 PureComponent 模块，在外层直接赋值，其实是在给基类中的 state 赋值，而在 constructor() 使用 this.state = {…} 进行初始化，实际指的是当前组件这个实例。以下两种赋值方法均可：</li>
</ul>
</li>
</ol>
</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">class TestContainer extends PureComponent {

    state = {
        content:&quot;1&quot;     //先执行
    }

    constructor(props){
        super(props);
        this.state={
            content:&quot;2&quot; //后执行，最终 this.state.content 为 2
        }
    }
    ...
}
</code></pre>
<pre><code>2. 绑定方法：this.handleClick = this.handleClick.bind(this);
    - 我所认为的是：在构造函数中进行方法的绑定后，在使用的时候可以减少匿名方法的产生，进而提高性能。  
</code></pre><ul>
<li>如果不需要初始化state，或者绑定方法，那么可以不用实现constructor(props)。</li>
<li>如果实现了这个方法，那么应当在方法体的最开始调用  super(props)，否则会导致 this.props 为 undefined 的问题。</li>
<li>不应在该方法中进行 “事件的订阅/引入有 side-effects 的方法”。（这种应该放在 componentDidMount() 中）</li>
</ul>
<blockquote>
<p>这里的 side-effects 方法，翻译出来是 “副作用” 的意思，个人觉得不妥，有点生涩，可能翻译成 “附加作用” 更为妥当。</p>
<p>它指的是，render() 方法应该只完成它的主要功能（如初始化 state 和 绑定方法 ），不应顺带完成其他的附加功能，如 统计，动画 等。</p>
</blockquote>
<h2 id="componentWillReceiveProps-nextProps"><a href="#componentWillReceiveProps-nextProps" class="headerlink" title="componentWillReceiveProps(nextProps)"></a><span id="componentWillReceiveProps">componentWillReceiveProps(nextProps)</span></h2><blockquote>
<p>该方法的应用场景：根据特定的 props 变换，来触发 state 的更新。如我们可能会有一个 canvas 用来表示页面 loading 百分比，这个时候就可以根据传入的 nextProps 中的百分比属性跟当前 state 的百分比属性做对比，若不一致，则更新，如下：</p>
</blockquote>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">componentWillReceiveProps(nextProps) {
    if (this.props.percent !== nextProps.percent) {
        this.setUpCircle(nextProps.percent);
    }
}
</code></pre>
<ul>
<li><strong>该方法在 React 16 版本中已经被标记为 unsafe ，虽然目前依然可以使用（即使用 componentWillReceiveProps 或者 UNSAFE_componentWillReceiveProps 均可），但在 React 17 中将会移除，<font color="red">不再建议使用【该方法将会被 getDerivedStateFromProps() 取代。】</font></strong>。</li>
<li>在组件对新(下一个)  props 执行任何操作之前，该方法会被触发，并将 下一个props 作为参数。</li>
<li>初始渲染时该方法不会被触发。</li>
<li>可以在该方法内调用 this.setState()，但并不建议调用，因为这会导致只要组件更新，这个方法就会被执行。</li>
</ul>
<h2 id="getDerivedStateFromProps-props-state"><a href="#getDerivedStateFromProps-props-state" class="headerlink" title="getDerivedStateFromProps(props, state)"></a><span id="getDerivedStateFromProps">getDerivedStateFromProps(props, state)</span></h2><blockquote>
<p>该方法的应用场景：使组件能够根据父组件传入的 props 来更新其自身的 state</p>
<p><em>在 16.4 版本之前，只有 props 更新才会触发该方法；但 FB 在 16.4 版本做了完善，目前 props 和 state 的更新均会触发该方法。</em></p>
</blockquote>
<ul>
<li><p>该方法的两个入参，分别表示：</p>
<ul>
<li>props：父组件传入的值。可能重命名为 nextProps 更为直观。</li>
<li>state：组件自身的state，相当于 this.state，可能重命名为 prevState 更好理解。</li>
</ul>
</li>
<li><p>该方法在调用 render() 方法前被触发。</p>
</li>
<li><p>如果父组件的 state 进行了更新，那么其子组件也会触发 getDerivedStateFromProps() 方法。</p>
</li>
<li><p>使用该方法的时候需要初始化 state ，否则在控制台中会出现警告信息，如下图：</p>
<ul>
<li><img src="https://pic.zhuliang.ltd/1101407-20181010112028416-475060693.png-c" alt></li>
</ul>
</li>
<li><p>使用该方法，需要在该方法中返回一个：对象/null：</p>
<ul>
<li>如果返回的是 对象 ，则会更新 state。</li>
<li>如果返回的是 null ，则表示不更新。</li>
</ul>
</li>
<li><p>该方法用于取代：componentWillReceiveProps(nextProps) 生命周期函数。</p>
<ul>
<li>getDerivedStateFromProps(props, state) 和 componentWillReceiveProps(nextProps) 的差异：<ul>
<li>componentWillReceiveProps(props, state)：仅在父组件更新时触发。</li>
<li>getDerivedStateFromProps(props, state)：除了父组件更新，其自身的 state 更新时也会触发。</li>
</ul>
</li>
</ul>
</li>
<li><p>该方法无法访问组件的实例，换句话说，不能在该方法内部，调用 this.state，this.myFunction() 等实例对象/方法。</p>
</li>
</ul>
<h2 id="componentWillMount"><a href="#componentWillMount" class="headerlink" title="componentWillMount()"></a>componentWillMount()</h2><blockquote>
<p>使用场景：可以在根组件的 componentWillMount 中做一些App的配置。</p>
</blockquote>
<ul>
<li><strong>该方法在 React 16 版本中已经被标记为 unsafe ，虽然目前依然可以使用（即使用 componentWillMount 或者 UNSAFE_componentWillMount 均可），但在 React 17 中将会移除，<font color="red">不再建议使用</font>。</strong></li>
<li>该方法将会被 getDerivedStateFromProps() 取代。</li>
<li>该方法是 SSR（server-side render） 渲染上唯一的生命周期函数。</li>
<li>不要在该方法中使用 RPC 请求加载组件的数据。</li>
<li>不能在该方法中调用 this.setState() 。</li>
</ul>
<h2 id="render"><a href="#render" class="headerlink" title="render()"></a><span id="render">render()</span></h2><ul>
<li>组件中唯一必须的方法。</li>
<li>render() 方法的返回类型目前有 string，number，boolean，null，portal 以及 fragments (带有key属性的数组)，eg：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">render() {
    return &quot;string&quot;;    //string
    return 123;         //number
    return true;        //如果返回的是 false，则什么都不渲染
    return null;        //如果返回的是 null，则什么都不渲染
    return ReactDOM.createPortal(&lt;MyComponent&gt;&lt;/MyComponent&gt;, domNode);//portal
    return [            
        &lt;div key=&quot;unique_1&quot;&gt;如果返回的是数组类型&lt;/div&gt;,
        &lt;span key=&quot;unique_2&quot;&gt;需要在每个html元素上加上 key 值&lt;/span&gt;,     //fragments
        &lt;p key=&quot;unique_3&quot;&gt;否则控制台会报错&lt;/p&gt;
    ]
}
</code></pre>
<ul>
<li>需要注意的是，组件在更新的时候，也会触发 render() 方法，但如果 shouldComponentUpdate() 返回的是false，则在 “更新阶段” ，render() 方法不会被触发。</li>
<li>该方法应为一个纯函数，除了做渲染的动作外，不应顺带完成其他动作（如 setState 等），即应避免 side-effects。</li>
<li>应尽量仅在 render() 这个生命周期函数中中从 this.props 和 this.state 中读取数据。</li>
</ul>
<h2 id="componentDidMount"><a href="#componentDidMount" class="headerlink" title="componentDidMount()"></a>componentDidMount()</h2><blockquote>
<p>使用场景：整体来说就是做“获取一些数据”，或者做必须要有 DOM 才能做的设置。</p>
<ul>
<li>只会触发一次：在组件被挂载到页面之后被触发，之后就不会再执行了（如页面的更新等都不会再触发，需注意）。</li>
<li>一般一些 rpc 请求会放到这个方法中进行调用。</li>
<li>一般事件的订阅会写在该方法中（但是不要忘记在组件unmount的时候进行取消订阅。）</li>
<li>一些需要初始化DOM节点的操作可以放在这个声明周期函数中进行。</li>
<li>可以在该方法中使用 this.setState()。但需要注意，该操作会在浏览器更新屏幕之前发生，对于用户而言是无感的（即在该方法中又额外调用了一次 this.setState ，导致 render() 触发了2次，但用户的感受依然是屏幕更新了一次）。不过要谨慎在该方法中调用 this.setState，会导致性能问题。</li>
</ul>
</blockquote>
<h2 id="Mounting-阶段，生命周期函数执行顺序如下图（包含子组件）"><a href="#Mounting-阶段，生命周期函数执行顺序如下图（包含子组件）" class="headerlink" title="Mounting 阶段，生命周期函数执行顺序如下图（包含子组件）"></a>Mounting 阶段，生命周期函数执行顺序如下图（包含子组件）</h2><p><img src="https://pic.zhuliang.ltd/1101407-20181011150606403-2024872434.png-c" alt></p>
<blockquote>
<p>排除了 UNSAFE 的方法。</p>
</blockquote>
<h1 id="Updating："><a href="#Updating：" class="headerlink" title="Updating："></a>Updating：</h1><h2 id="componentWillReceiveProps-nextProps-1"><a href="#componentWillReceiveProps-nextProps-1" class="headerlink" title="componentWillReceiveProps(nextProps)"></a>componentWillReceiveProps(nextProps)</h2><ul>
<li>不再复述，具体 <a href="#componentWillReceiveProps">点此跳转</a></li>
</ul>
<h2 id="getDerivedStateFromProps-props-state-1"><a href="#getDerivedStateFromProps-props-state-1" class="headerlink" title="getDerivedStateFromProps(props, state)"></a>getDerivedStateFromProps(props, state)</h2><ul>
<li>不再复述，具体 <a href="#getDerivedStateFromProps">点此跳转</a></li>
</ul>
<h2 id="shouldComponentUpdate-nextProps-nextState"><a href="#shouldComponentUpdate-nextProps-nextState" class="headerlink" title="shouldComponentUpdate(nextProps, nextState)"></a>shouldComponentUpdate(nextProps, nextState)</h2><blockquote>
<p>使用场景：一般用于精确控制组件是否需要重新渲染（这个方法一般用于性能优化），在绝大多数情况下，每次 state 的更改都应该让组件重新渲染。</p>
<ul>
<li>只要有一个字段进行了更新，那么其他所有字段都会进行更新，这个会减慢页面速度。（通过 shouldComponentUpdate ，允许我们只有当我们关心的 props 更新的时候，才进行组件的更新）。</li>
<li>但需要谨慎使用，因为一旦你自己忘记实现了这个方法，可能会导致你的 react 组件无法正常更新。</li>
</ul>
</blockquote>
<ul>
<li><p>该方法返回的数据类型是一个 boolean ，用于决定组件是否应该更新。</p>
<ul>
<li>默认返回 true。</li>
<li>当返回 false 的时候，阻止的是当前的组件进行更新，对于当前组件的 “子组件” ，并不受影响。</li>
</ul>
</li>
<li><p>不应该在继承于 PureComponent 的组件中显示实现 shouldComponentUpdate()，控制台会有警告。</p>
</li>
<li><p>目前，当该方法返回 false 的时候，componentWillUpdate()、更新时的 render() 以及 componentDidUpdate() 都将不会被触发。（FB在官网上说后期有可能会调整该方法：即使返回 false，依然有可能会触发再次渲染，需留个心）</p>
</li>
<li><p>什么时候该方法不会被触发？</p>
<ul>
<li>组件第一次渲染时。</li>
<li>在组件中调用 this.forceUpdate() 时。</li>
</ul>
</li>
<li><p>个人建议：</p>
<ul>
<li>该方法应尽量不去手动实现。</li>
<li>不应使用该方法来做一些防止组件渲染的操作，这可能会导致一些莫名的 bug，如组件明明应该更新，但是却没有更新。</li>
<li>优先可以考虑继承 PureComponent 而非 Component 来优化组件而不是手动实现 shouldComponentUpdate。</li>
</ul>
</li>
<li><p>不能在这个方法中使用 this.setState()。</p>
</li>
</ul>
<h2 id="componentWillUpdate-nextProps-nextState"><a href="#componentWillUpdate-nextProps-nextState" class="headerlink" title="componentWillUpdate(nextProps, nextState)"></a>componentWillUpdate(nextProps, nextState)</h2><blockquote>
<p>使用场景：当你实现了 shouldComponentUpdate (返回 true 时) 并且在 props 更改时需要执行某些操作的时候，那么 componentWillUpdate 这个方法还是有点意义的，但个人认为，作用不是太大，反倒是增加了理解的复杂度，被删除也是情理之中。</p>
</blockquote>
<ul>
<li><strong>该方法在 React 16 版本中已经被标记为 unsafe，虽然目前依然可以使用（即使用 componentWillUpdate 或者 UNSAFE_componentWillUpdate() 均可），但在 React 17 中将会移除，<font color="red">不再建议使用</font>。</strong></li>
<li>从功能上来说，componentWillUpdate(nextProps, nextState) 跟 componentWillReceiveProps(nextProps) 基本相同，只是前者不允许调用 this.setState()。但是，这2个方法</li>
<li>当 shouldComponentUpdate 返回 false 时， 该生命周期函数将不会被触发。</li>
<li>不能使用 setState。</li>
</ul>
<h2 id="render-1"><a href="#render-1" class="headerlink" title="render()"></a>render()</h2><ul>
<li>不再复述，具体 <a href="#render">点此跳转</a></li>
</ul>
<h2 id="componentDidUpdate-prevProps-prevState-snapshot"><a href="#componentDidUpdate-prevProps-prevState-snapshot" class="headerlink" title="componentDidUpdate(prevProps, prevState, snapshot)"></a>componentDidUpdate(prevProps, prevState, snapshot)</h2><blockquote>
<p>使用场景：</p>
<ul>
<li>一般在这个方法中进行 RPC 请求。（可以比较下先前的 props 和当前的 props 是否一致，若一致，则可以不用请求网络数据。）</li>
<li>如果想要在DOM自身更新之后做一些动作（如重新排列网格等），那么可以在这个方法中进行。</li>
</ul>
</blockquote>
<ul>
<li>该生命周期函数在组件更新完成后立即执行。组件第一次渲染的时候，该生命周期函数不会被触发。</li>
<li>可以做跟 componentDidMount 中所作的相同的事情：如重置布局，重绘画布等</li>
<li>可以使用 setState。</li>
</ul>
<h2 id="Updating-阶段，生命周期函数执行顺序如下图（包含子组件）"><a href="#Updating-阶段，生命周期函数执行顺序如下图（包含子组件）" class="headerlink" title="Updating 阶段，生命周期函数执行顺序如下图（包含子组件）"></a>Updating 阶段，生命周期函数执行顺序如下图（包含子组件）</h2><p><img src="https://pic.zhuliang.ltd/1101407-20181011175449202-705885730.png-c" alt></p>
<blockquote>
<p>排除了 UNSAFE 的方法。</p>
</blockquote>
<h1 id="Unmounting："><a href="#Unmounting：" class="headerlink" title="Unmounting："></a>Unmounting：</h1><h2 id="componentWillUnmount"><a href="#componentWillUnmount" class="headerlink" title="componentWillUnmount()"></a>componentWillUnmount()</h2><ul>
<li>该方法在组件将要被卸载的时候触发。</li>
<li>另外，在渲染期间，当前组件/其子组件的 componentDidMount() 函数发生错误时，该方法也将被触发。</li>
<li>eg：<ul>
<li>我刻意在”嵌套在子组件中的组件”中的 componentDidMount() 方法中，抛出了一个异常，此时该组件往上的父级组件均触发了 componentWillUnmount()</li>
<li><img src="https://pic.zhuliang.ltd/1101407-20181011180929303-917320786.png-c" alt><ul>
<li>我尝试在 construct()，componentDidUpdate() 等生命周期函数中抛出异常，均不会触发 componentWillUnmount()。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="Unmounting-阶段，生命周期函数执行顺序如下图（包含子组件）"><a href="#Unmounting-阶段，生命周期函数执行顺序如下图（包含子组件）" class="headerlink" title="Unmounting 阶段，生命周期函数执行顺序如下图（包含子组件）"></a>Unmounting 阶段，生命周期函数执行顺序如下图（包含子组件）</h2><p><img src="https://pic.zhuliang.ltd/1101407-20181011180056502-187797890.png-c" alt></p>
<p>这里需要注意下，该函数是 <strong>will</strong> unmount，所以触发顺序上是从父组件到子组件，但释放顺序上，是最内层的子组件先释放，最终最外层的根组件才释放。</p>
<h1 id="额外的几个方法说明："><a href="#额外的几个方法说明：" class="headerlink" title="额外的几个方法说明："></a>额外的几个方法说明：</h1><h2 id="componentDidCatch-error-info"><a href="#componentDidCatch-error-info" class="headerlink" title="componentDidCatch(error,info)"></a>componentDidCatch(error,info)</h2><blockquote>
<p>使用场景：UI中的一些JS错误不应该使整个App崩溃，为了解决这个问题，React 16 中引入了 Error Boundary(错误边界) 这个概念，旨在解决允许页面的部分组件异常但不影响 App 的渲染。可以认为是组件中的 try-catch。而为了实现这个功能，就需要借助 componentDidCatch() 这个生命周期函数。</p>
</blockquote>
<ul>
<li>Error Boundaris 是自定义的 react 的组件，这些组件可以捕获其子组件的异常，并显示子组件错误时的替代组件内容，而 App 不会崩溃。</li>
<li>怎么样的组件可以认为是 Error Boundary 组件？<ul>
<li>只要该组件内部实现了 componentDidCatch 这个方法就可以认为是 ErrorBoundary 组件，如：</li>
</ul>
</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">...
class MyErrorBoundary extends Component{
    ...
    componentDidCatch(error,info){
        ...
    }
    ...
}
</code></pre>
<ul>
<li>一个例子：当子组件发生错误时，显示 “sth wrong here.”<ul>
<li>MyErrorBoundary:</li>
</ul>
</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React, { Component } from &#39;react&#39;;
class MyErrorBoundary extends Component {
    state = {
        isError: false
    }
    render() {
        if (this.state.isError) {
            return (&lt;div&gt;sth wrong here.&lt;/div&gt;);
        }
        return this.props.children;
    }
    componentDidCatch(error, info) {
        console.log(error, info);
        this.setState({
            isError = true
        });
        //也可以做其他的一些事情，如 日志，异常数统计 等
    }
}
export default MyErrorBoundary;
</code></pre>
<pre><code>- App.js
</code></pre><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">...
render(){
    return (
    ...
    &lt;MyErrorBoundary&gt;
        &lt;OtherComponent /&gt;
    &lt;/MyErrorBoundary&gt;
    ...
    );
}
...
</code></pre>
<ul>
<li>ErrorBoundary 组件只能捕获其子组件，无法捕获其自身的异常。</li>
<li>为什么用 ErrorBoundary 这样的组件？直接在代码中 try-catch 不是很直观？<ul>
<li>这个问题，就仁智各见了， try-catch 面向的是 代码，而 ErrorBoundary 面向的是组件：这里我想用一个后端例子来说明，在写后端代码时（比如一个 Web Api） ，你可以在具体业务代码中使用 try-catch 来对可能出现异常的代码块做处理，但有的时候，你可能想针对所有 “方法” 做异常处理，这个时候使用 AOP 的方式来写一个异常处理的方法更好（或者可以认为是 Exception filters 这样的自定义类）</li>
</ul>
</li>
<li>注意事项：若要查看 ErrorBoundary 的效果，无法在开发模式下进行（即 mode=development）【或者直接运行 npm start，是不会出效果的，会显示具体的错误信息。】，有两种方法可以解决：<ul>
<li><ol>
<li>使用 npm run build 命令将项目打包后发布到服务器上进行；</li>
</ol>
</li>
<li><ol start="2">
<li>将项目的模式从 development 调整为 production<ul>
<li>需注意，若是使用 create-react-app 来创建的项目，配置文件需要解包后才能进行编辑，运行命令： npm run eject，解包后调整”/scripts/build.js”，将 process.env.NODE_ENV 赋值为 production，之后再运行 npm start 的时候就能看到上面示例代码的效果。</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<h2 id="this-setState"><a href="#this-setState" class="headerlink" title="this.setState()"></a>this.setState()</h2><ul>
<li>用来设置 state 的值。</li>
<li>在 16.3 开始，FB 建议使用 setState 的异步函数写法，如：<br>原先我们在使用的时候是直接进行赋值：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">inputHandler = (e) =&gt; {
    this.setState({
        inputValue: e.target.value              //不再这么做
    });
)
</code></pre>
<p>而是改用：</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">inputHandler = (e) =&gt; {
    let value = e.target.value;
    this.setState(() =&gt; {
        return {inputValue: value};   //使用方法的形式，最终再返回一个对象，这里需要注意下，这么写是异步的，但存在一个问题，即输入的内容，需要再方法外层先获取到：如这里的value。
    })
}
</code></pre>
<ul>
<li>异步写法中，该方法提供了一个回调函数，通过该回调函数，可以确保只有等到 setState 触发完成之后，才会执行回调的方法（另外一个可以确保在 setState 执行完成之后再执行的点是 componentDidUpdate 方法），如：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">this.setState((prevState, props)=&gt;{
    ...    
}, callback);
</code></pre>
<pre><code>- 该方法提供了2个入参，prevState 和 props，前者相当于是 this.state。
- 只有当 setState 方法的第一个参数执行完成之后，才会执行 callback方法。
- 需要注意的是，因为 setState 是一个异步方法，所以在赋值的时候需要注意下，如果需要从表单或者其他地方获取值赋值给 state 的某一个属性，需要先把这个值在 setState 方法之前赋给一个变量，再在 setState 方法中使用这个变量。如
</code></pre><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">let name = e.target.value;
this.setState((prevState, props)=&gt;{
    return {
        userName : name  //不能够直接 userName:e.target.value，异步方法中获取不到当前上下文
    }    
}, callback);
</code></pre>
<ul>
<li>补充：对于 setState ，其不一定在调用 setState 的时候就立即触发这个动作。为了性能，react 会自行判断，将组件的所有 setState 在同一个时间点一同执行（批量执行），而非调用一次就执行一次。</li>
<li>每一次 setState 都会导致组件的再次渲染，除非 shouldComponentUpdate 返回 false。</li>
</ul>
<h2 id="this-forceUpdate"><a href="#this-forceUpdate" class="headerlink" title="this.forceUpdate()"></a>this.forceUpdate()</h2><ul>
<li>调用forceUpdate()的时候，将会跳过 shouldComponentUpdate() 而直接重新 render() 组件。</li>
<li>父组件中调用 forceUpdate 亦会导致子组件的生命周期函数被触发（包括 componentDidUpdate）。</li>
<li>正常情况下，这个方法很少使用。</li>
</ul>

      

      <div>
        
        <div style="background-color: rgb(255, 23, 0);user-select: auto;height: 3px;width:100%;margin-top: 40px;margin-bottom:15px;"></div>

<!-- <p>常言道：学然后知不足，教然后知困。</p>
<p>我知道你的焦虑，一起共进加油：P</p>
<p>关不关注都无所谓，会根据生活节奏紧凑度定期分享些开发经验、搬砖生涯、痛点、感悟。</p>
<p>
        <img src="https://pic.zhuliang.ltd/20191213133908.gif" alt="欢迎关注我的订阅号：P" />
</p> -->
<ul class="post-copyright" style="margin-left:0px;">
    <li class="post-copyright-author">
        <strong>本文作者：</strong>积慕
    </li>
    <li class="post-copyright-link">
        <strong>本文链接：</strong>
        <a href="/2018/10/frontend/react-16-5-2-lifecycle.html" title="React 生命周期函数深入浅出">https://blog.zhuliang.ltd/2018/10/frontend/react-16-5-2-lifecycle.html</a>
    </li>
    <li class="post-copyright-license">
        <strong>版权： </strong>
        本站文章均采用 <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/cn/" rel="external nofollow"
            target="_blank">CC BY-NC-SA 3.0 CN</a> 许可协议，请勿用于商业，转载注明出处！
    </li>
</ul>
        
      </div>
    </div>

    <footer>
      
      
  
  <div class="categories">
    <a href="/categories/frontend/">frontend</a>
  </div>

      
  
  <div class="tags">
    <a href="/tags/react/">react</a>, <a href="/tags/基础知识/">基础知识</a>, <a href="/tags/生命周期/">生命周期</a>
  </div>

      <!-- 
  <div class="addthis addthis_toolbox addthis_default_style">
    
    
      <a class="addthis_button_tweet"></a>
    
    
      <a class="addthis_button_google_plusone" g:plusone:size="medium"></a>
    
    
      <a class="addthis_button_pinterest_pinit" pi:pinit:layout="horizontal"></a>
    
    <a class="addthis_counter addthis_pill_style"></a>
  </div>
  <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js"></script>
 -->
      
      <div class="clearfix"></div>
    </footer>
  </div>
</article>

<!-- 
<section id="comment">
  <h1 class="title">留言</h1>

  <div id="vcomments" style="background-color: #fff"></div>
  <script>
    new Valine({
      el: '#vcomments',
      appId: 'aTPKmG6tjDo3CbIXzAPzrj4X-gzGzoHsz',
      appKey: 'hNgWFneNpEcMnBJclPQnDgsd',
      notify: true,
      verify: true, //验证码
      avatar: 'monsterid', //https://valine.js.org/avatar.html
      placeholder: '请输入你的评论'
    })
  </script>
</section>
 --> <!--启用/关闭 LeanCloud 评论--></div></div>
    <aside id="sidebar" class="alignright">
  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-category"></i>&nbsp;&nbsp;分类</h3>
  <ul class="entry">
  
    <li><a href="/categories/Obsidian/">Obsidian</a><small>1</small></li>
  
    <li><a href="/categories/PowerUser/">PowerUser</a><small>4</small></li>
  
    <li><a href="/categories/backend/">backend</a><small>57</small></li>
  
    <li><a href="/categories/frontend/">frontend</a><small>9</small></li>
  
    <li><a href="/categories/operations/">operations</a><small>5</small></li>
  
    <li><a href="/categories/提前退休/">提前退休</a><small>2</small></li>
  
  </ul>
</div>


  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-new3"></i>&nbsp;&nbsp;最新文章</h3>
  <ul class="entry">
    
      <li>
        <a href="/2024/11/PowerUser/install-docker-in-pve-lxc.html">1. 通过 PVE LXC 搭建 docker 环境</a>
      </li>
    
      <li>
        <a href="/2024/11/Obsidian/obsidian-render-pie-chart.html">2. 2 套用于 obsidian 的生成时间占用饼图脚本示例，基于 templater， charts，echarts+dataview</a>
      </li>
    
      <li>
        <a href="/2024/10/PowerUser/fix-ios18-samba-readonly-issue.html">3. 解决 ios 18（包括ipadOS 18） 下通过文件(files app)连接 samba 服务器，目录变成只读权限的问题</a>
      </li>
    
      <li>
        <a href="/2023/07/operations/how-to-use-pm2.html">4. PM2 的安装、配置和使用</a>
      </li>
    
      <li>
        <a href="/2023/07/backend/expose-intranet-netcoreapi-to-internet.html">5. 通过 DDNS+显性URL 把内网部署的.NET CORE WEBAPI 项目暴露到公网中</a>
      </li>
    
  </ul>
</div>


  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-tag"></i>&nbsp;&nbsp;标签</h3>
  <ul class="entry">
  
    <li><a href="/tags/net-core/">.net core</a><small>12</small></li>
  
    <li><a href="/tags/Debian/">Debian</a><small>2</small></li>
  
    <li><a href="/tags/Docker/">Docker</a><small>1</small></li>
  
    <li><a href="/tags/Elasticsearch/">Elasticsearch</a><small>35</small></li>
  
    <li><a href="/tags/Elasticsearch-Nest系列/">Elasticsearch.Nest系列</a><small>35</small></li>
  
    <li><a href="/tags/Obsidian/">Obsidian</a><small>1</small></li>
  
    <li><a href="/tags/PVE/">PVE</a><small>2</small></li>
  
    <li><a href="/tags/StackExchange-Redis系列/">StackExchange.Redis系列</a><small>12</small></li>
  
    <li><a href="/tags/centos/">centos</a><small>3</small></li>
  
    <li><a href="/tags/flex/">flex</a><small>1</small></li>
  
    <li><a href="/tags/ios/">ios</a><small>1</small></li>
  
    <li><a href="/tags/javascript/">javascript</a><small>1</small></li>
  
    <li><a href="/tags/linux/">linux</a><small>4</small></li>
  
    <li><a href="/tags/nginx/">nginx</a><small>2</small></li>
  
    <li><a href="/tags/nuget/">nuget</a><small>2</small></li>
  
    <li><a href="/tags/react/">react</a><small>6</small></li>
  
    <li><a href="/tags/styled-components/">styled-components</a><small>1</small></li>
  
    <li><a href="/tags/内网穿透/">内网穿透</a><small>4</small></li>
  
    <li><a href="/tags/培训讲义/">培训讲义</a><small>2</small></li>
  
    <li><a href="/tags/基础知识/">基础知识</a><small>2</small></li>
  
    <li><a href="/tags/性能优化/">性能优化</a><small>2</small></li>
  
    <li><a href="/tags/教程/">教程</a><small>64</small></li>
  
    <li><a href="/tags/理财/">理财</a><small>2</small></li>
  
    <li><a href="/tags/生命周期/">生命周期</a><small>1</small></li>
  
    <li><a href="/tags/经验/">经验</a><small>4</small></li>
  
  </ul>
</div>


  
  <script type="text/javascript" charset="utf-8" src="/js/tagcloud.js"></script>
  <script type="text/javascript" charset="utf-8" src="/js/tagcanvas.js"></script>
  <div class="widget tag">
    <h3 class="title"><i class="iconfont icon-cloud1"></i>&nbsp;&nbsp;标签云</h3>
    <div id="myCanvasContainer" class="widget tagcloud">
      <canvas width="250" height="250" id="resCanvas" style="width:100%">
        <a href="/tags/net-core/" style="font-size: 17.14px;">.net core</a> <a href="/tags/Debian/" style="font-size: 11.43px;">Debian</a> <a href="/tags/Docker/" style="font-size: 10px;">Docker</a> <a href="/tags/Elasticsearch/" style="font-size: 18.57px;">Elasticsearch</a> <a href="/tags/Elasticsearch-Nest系列/" style="font-size: 18.57px;">Elasticsearch.Nest系列</a> <a href="/tags/Obsidian/" style="font-size: 10px;">Obsidian</a> <a href="/tags/PVE/" style="font-size: 11.43px;">PVE</a> <a href="/tags/StackExchange-Redis系列/" style="font-size: 17.14px;">StackExchange.Redis系列</a> <a href="/tags/centos/" style="font-size: 12.86px;">centos</a> <a href="/tags/flex/" style="font-size: 10px;">flex</a> <a href="/tags/ios/" style="font-size: 10px;">ios</a> <a href="/tags/javascript/" style="font-size: 10px;">javascript</a> <a href="/tags/linux/" style="font-size: 14.29px;">linux</a> <a href="/tags/nginx/" style="font-size: 11.43px;">nginx</a> <a href="/tags/nuget/" style="font-size: 11.43px;">nuget</a> <a href="/tags/react/" style="font-size: 15.71px;">react</a> <a href="/tags/styled-components/" style="font-size: 10px;">styled-components</a> <a href="/tags/内网穿透/" style="font-size: 14.29px;">内网穿透</a> <a href="/tags/培训讲义/" style="font-size: 11.43px;">培训讲义</a> <a href="/tags/基础知识/" style="font-size: 11.43px;">基础知识</a> <a href="/tags/性能优化/" style="font-size: 11.43px;">性能优化</a> <a href="/tags/教程/" style="font-size: 20px;">教程</a> <a href="/tags/理财/" style="font-size: 11.43px;">理财</a> <a href="/tags/生命周期/" style="font-size: 10px;">生命周期</a> <a href="/tags/经验/" style="font-size: 14.29px;">经验</a>
      </canvas>
    </div>
  </div>




</aside>
    <div class="clearfix"></div>
  </div>
  <footer id="footer" class="inner"><div class="alignleft">

  
  &copy; 2018 - 2024 &nbsp;<a href='/about'>积慕</a>
  
  &nbsp;&nbsp;<a href='https://www.cnblogs.com/deepthought/'>我的博客园地址</a>
  &nbsp;&nbsp;finalclassic#outlook.com
</div>

<div class="clearfix"></div>
<div>
  本站主题基于：<a href='https://github.com/hexojs/hexo-theme-light' rel="external nofollow">hexo-theme-light</a>
</div>
<div id="busuanzi_container_site_pv">
  本站总访问量&nbsp;<span id="busuanzi_value_site_pv"></span>&nbsp;次
</div>
<div style="width:300px;margin:0 auto; padding:20px 0;">
  <a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=31011702004805" style="display:inline-block;text-decoration:none;height:20px;line-height:20px;">
    <img src="https://pic.zhuliang.ltd/20200102164648.png" style="float:left;"><p style="float:left;height:20px;line-height:20px;margin: 0px 0px 0px 5px; color:#939393;">沪公网安备 31011702004805号</p></a>
</div></footer>
  <script src="/js/jquery-2.0.3.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>




<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" media="screen" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
(function($){
  $('.fancybox').fancybox();
})(jQuery);
</script>


<script type="text/javascript">
hljs.initHighlightingOnLoad();
</script>

<!-- <script src="/js/busuanzi.pure.mini.js"></script> -->
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<script>
    var _hmt = _hmt || [];
    (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?ea793b9c8d6b31df666a6bc15038e246";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
    })();
</script>
<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        }
        else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>
</body>
</html>
